In recent times, the field of agriculture has been in urgent need of modernizing, since the amount of manual work people need to put in to check if plants are growing correctly is still highly extensive. Despite several advances in agricultural technology, people working in the agricultural industry still need to have the ability to sort and recognize different plants and weeds, which takes a lot of time and effort in the long term. The potential is ripe for this trillion-dollar industry to be greatly impacted by technological innovations that cut down on the requirement for manual labor, and this is where Artificial Intelligence can actually benefit the workers in this field, as the time and energy required to identify plant seedlings will be greatly shortened by the use of AI and Deep Learning. The ability to do so far more efficiently and even more effectively than experienced manual labor, could lead to better crop yields, the freeing up of human inolvement for higher-order agricultural decision making, and in the long term will result in more sustainable environmental practices in agriculture as well.
The aim of this project is to Build a Convolutional Neural Network to classify plant seedlings into their respective categories.
The Aarhus University Signal Processing group, in collaboration with the University of Southern Denmark, has recently released a dataset containing images of unique plants belonging to 12 different species.
Due to the large volume of data, the images were converted to the images.npy file and the labels are also put into Labels.csv, so that you can work on the data/project seamlessly without having to worry about the high data volume.
The goal of the project is to create a classifier capable of determining a plant's species from an image.
List of Species
import os
import numpy as np # Importing numpy for Matrix Operations
import pandas as pd # Importing pandas to read CSV files
import matplotlib.pyplot as plt # Importting matplotlib for Plotting and visualizing images
import math # Importing math module to perform mathematical operations
import cv2 # Importing openCV for image processing
import seaborn as sns # Importing seaborn to plot graphs
# Tensorflow modules
import tensorflow as tf
from tensorflow.keras.preprocessing.image import ImageDataGenerator # Importing the ImageDataGenerator for data augmentation
from tensorflow.keras.models import Sequential # Importing the sequential module to define a sequential model
from tensorflow.keras.layers import Dense,Dropout,Flatten,Conv2D,MaxPooling2D,BatchNormalization # Defining all the layers to build our CNN Model
from tensorflow.keras.optimizers import Adam,SGD # Importing the optimizers which can be used in our model
from sklearn import preprocessing # Importing the preprocessing module to preprocess the data
from sklearn.model_selection import train_test_split # Importing train_test_split function to split the data into train and test
from sklearn.metrics import confusion_matrix # Importing confusion_matrix to plot the confusion matrix
from sklearn.preprocessing import LabelBinarizer
# Display images using OpenCV
from google.colab.patches import cv2_imshow # Importing cv2_imshow from google.patches to display images
from sklearn.model_selection import train_test_split
from tensorflow.keras import backend
from keras.callbacks import ReduceLROnPlateau
import random
# Ignore warnings
import warnings
warnings.filterwarnings('ignore')
# Mount Google drive to access the dataset
# Run the following lines for Google Colab to mount your Google drive
from google.colab import drive
drive.mount('/content/drive')
Mounted at /content/drive
Problem Statement Definition:
In the agricultural sector, a significant challenge lies in the time-intensive and laborious process of identifying different plants and weeds, a task essential for effective crop management. Although technological advancements have been made in agriculture, there remains a substantial need for manual effort in sorting and recognizing plant species. This not only consumes considerable time but also limits the potential for more strategic and higher-order decision-making in agricultural practices.
The objective of this project is to develop and deploy a Convolutional Neural Network capable of accurately classifying plant seedlings into their respective categories.
# Load the image file of dataset
images = np.load('/content/drive/My Drive/Computer_Vision/images.npy')
# Load the labels file of dataset
labels = pd.read_csv('/content/drive/My Drive/Computer_Vision/Labels.csv')
# Display the shape and information of the dataset
print(images.shape)
print(labels.shape)
(4750, 128, 128, 3) (4750, 1)
The images dataset is a 4-dimensional numpy array with a shape of (4750, 128, 128, 3). This indicates that there are 4750 images, each with a resolution of 128x128 pixels, and each pixel has 3 color channels (RGB).
The labels dataset, loaded from a CSV file, is a pandas DataFrame with a shape of (4750, 1). This implies that there are 4750 rows, each corresponding to an image, and there is 1 column, likely representing the associated 'Label'.
Note: Consolidated answers are provided at the end of the EDA.
# Plot random images from each of the classes
def plot_images(images,labels):
num_classes=10
categories=np.unique(labels)
keys=dict(labels['Label'])
rows = 3
cols = 4
# Define the figure size to 10x8
fig = plt.figure(figsize=(10, 8))
for i in range(cols):
for j in range(rows):
random_index = np.random.randint(0, len(labels))
ax = fig.add_subplot(rows, cols, i * rows + j + 1)
ax.imshow(images[random_index, :])
ax.set_title(keys[random_index])
plt.show()
# Plot the images with their labels
plot_images(images,labels)
# Visualize the distribution for each category
sns.countplot(x=labels['Label'])
plt.xticks(rotation='vertical')
plt.show()
Based on the Exploratory Data Analysis (EDA) of the datasets, here are answers to the questions:
How are these different category plant images different from each other?
Is the dataset provided an imbalance? (Check with using bar plots)?
# Convert the images from BGR to RGB using cvtColor function of OpenCV
for i in range(len(images)):
images[i] = cv2.cvtColor(images[i], cv2.COLOR_BGR2RGB)
# Print the first 5 converted RGB images
for i in range(5):
plt.imshow(images[i])
plt.title(f'Image {i+1}')
plt.axis('off') # Turn off axis numbers and ticks
plt.show()
As the size of the images is large, it may be computationally expensive to train on these larger images; therefore, it is preferable to reduce the image size from 128 to 64.
# Display the first 5 images before resizing the images
for i in range(5):
plt.imshow(images[i])
plt.title(f'Original Image {i+1}')
plt.axis('off') # Hide axis labels
plt.show()
# Resize the images from 128x128 to 64x64 pixels to reduce computation requirement during model training
images_decreased=[]
height = 64
width = 64
dimensions = (width, height)
for i in range(len(images)):
images_decreased.append( cv2.resize(images[i], dimensions, interpolation=cv2.INTER_LINEAR))
# Display the first 5 images after resizing the images
for i in range(5):
plt.imshow(images_decreased[i])
plt.title(f'Resized Image {i+1}')
plt.axis('off') # Hide axis labels
plt.show()
Overall, resizing images is a common practice to balance between input detail and the practicality of training neural networks, especially when dealing with large datasets.
Split the dataset
# Split the dataset into three parts: use 10% of our data for testing, 10% of our data for validation and 80% of our data for training
# First split: Separate out the test set
X_temp, X_test, y_temp, y_test = train_test_split(np.array(images_decreased), labels, test_size=0.1, random_state=42, stratify=labels)
# Second split: Separate the remaining data into training and validation sets
X_train, X_val, y_train, y_val = train_test_split(X_temp, y_temp, test_size=0.1, random_state=42, stratify=y_temp)
# Display the dimensions of the training, validation, and test datasets
print(X_train.shape, y_train.shape)
print(X_val.shape, y_val.shape)
print(X_test.shape, y_test.shape)
(3847, 64, 64, 3) (3847, 1) (428, 64, 64, 3) (428, 1) (475, 64, 64, 3) (475, 1)
# Convert labels from names to One-Hot vectors using the LabelBinarizer encoding method
enc = LabelBinarizer()
y_train_encoded = enc.fit_transform(y_train)
y_val_encoded = enc.transform(y_val)
y_test_encoded = enc.transform(y_test)
# Display the dimensions of the training, validation, and test data after encoding the target labels
y_train_encoded.shape, y_val_encoded.shape, y_test_encoded.shape
((3847, 12), (428, 12), (475, 12))
# Normalize the image pixels of train, test and validation data
X_train_normalized = X_train.astype('float32') / 255.0
X_val_normalized = X_val.astype('float32') / 255.0
X_test_normalized = X_test.astype('float32') / 255.0
Normalization in the context of the CNN Plant Seedlings Classification project is crucial for the following reasons:
Overall, normalization is a key preprocessing step for enhancing model performance and stability in image classification tasks.
# Clear the backend
backend.clear_session()
# Fix the seed for random number generators to get the same data in every run
np.random.seed(42)
random.seed(42)
tf.random.set_seed(42)
# Initialize a sequential model
model1 = Sequential()
# Add the first conv layer
model1.add(Conv2D(128, (3, 3), activation='relu', padding="same", input_shape=(64, 64, 3)))
# Add max pooling to reduce the size of output of first conv layer
model1.add(MaxPooling2D((2, 2), padding = 'same'))
# Create two similar convolution and max-pooling layers
model1.add(Conv2D(64, (3, 3), activation='relu', padding="same"))
model1.add(MaxPooling2D((2, 2), padding = 'same'))
model1.add(Conv2D(32, (3, 3), activation='relu', padding="same"))
model1.add(MaxPooling2D((2, 2), padding = 'same'))
# Flatten the output
model1.add(Flatten())
# Add a fully connected dense layer
model1.add(Dense(16, activation='relu'))
model1.add(Dropout(0.3))
# Output layer
model1.add(Dense(12, activation='softmax'))
# Adam Optimizer
opt = Adam()
# Compile the model
model1.compile(optimizer=opt, loss='categorical_crossentropy', metrics=['accuracy'])
# Generate the summary of the model
model1.summary()
Model: "sequential"
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
conv2d (Conv2D) (None, 64, 64, 128) 3584
max_pooling2d (MaxPooling2 (None, 32, 32, 128) 0
D)
conv2d_1 (Conv2D) (None, 32, 32, 64) 73792
max_pooling2d_1 (MaxPoolin (None, 16, 16, 64) 0
g2D)
conv2d_2 (Conv2D) (None, 16, 16, 32) 18464
max_pooling2d_2 (MaxPoolin (None, 8, 8, 32) 0
g2D)
flatten (Flatten) (None, 2048) 0
dense (Dense) (None, 16) 32784
dropout (Dropout) (None, 16) 0
dense_1 (Dense) (None, 12) 204
=================================================================
Total params: 128828 (503.23 KB)
Trainable params: 128828 (503.23 KB)
Non-trainable params: 0 (0.00 Byte)
_________________________________________________________________
The Model1 setup indicates a three-layer convolutional neural network with max pooling, designed for a 12-class classification problem. It has a total of approximately 128.8K parameters, which is relatively modest, likely making it efficient to train. The sequential increase in the number of filters in convolutional layers allows for hierarchical feature extraction, which is beneficial for image classification tasks. The dropout layer is included to prevent overfitting, suggesting the model is designed with generalization in mind.
# Fit the model on the train data
history_1 = model1.fit(
X_train_normalized, y_train_encoded,
epochs=30,
validation_data=(X_val_normalized, y_val_encoded),
batch_size=32,
verbose=2
)
Epoch 1/30 121/121 - 8s - loss: 2.4520 - accuracy: 0.1133 - val_loss: 2.4379 - val_accuracy: 0.1285 - 8s/epoch - 65ms/step Epoch 2/30 121/121 - 1s - loss: 2.3262 - accuracy: 0.1983 - val_loss: 2.1111 - val_accuracy: 0.3505 - 1s/epoch - 11ms/step Epoch 3/30 121/121 - 1s - loss: 2.0336 - accuracy: 0.3039 - val_loss: 1.7779 - val_accuracy: 0.4112 - 1s/epoch - 11ms/step Epoch 4/30 121/121 - 2s - loss: 1.9072 - accuracy: 0.3332 - val_loss: 1.6982 - val_accuracy: 0.4463 - 2s/epoch - 18ms/step Epoch 5/30 121/121 - 1s - loss: 1.7803 - accuracy: 0.3629 - val_loss: 1.5991 - val_accuracy: 0.4720 - 1s/epoch - 12ms/step Epoch 6/30 121/121 - 1s - loss: 1.6947 - accuracy: 0.3865 - val_loss: 1.4596 - val_accuracy: 0.5304 - 1s/epoch - 9ms/step Epoch 7/30 121/121 - 1s - loss: 1.6598 - accuracy: 0.3902 - val_loss: 1.3470 - val_accuracy: 0.5467 - 1s/epoch - 9ms/step Epoch 8/30 121/121 - 1s - loss: 1.5899 - accuracy: 0.4001 - val_loss: 1.2683 - val_accuracy: 0.5584 - 1s/epoch - 9ms/step Epoch 9/30 121/121 - 1s - loss: 1.5472 - accuracy: 0.4284 - val_loss: 1.2690 - val_accuracy: 0.5841 - 1s/epoch - 9ms/step Epoch 10/30 121/121 - 1s - loss: 1.4917 - accuracy: 0.4507 - val_loss: 1.2443 - val_accuracy: 0.5678 - 1s/epoch - 10ms/step Epoch 11/30 121/121 - 2s - loss: 1.4261 - accuracy: 0.4822 - val_loss: 1.1995 - val_accuracy: 0.6005 - 2s/epoch - 13ms/step Epoch 12/30 121/121 - 2s - loss: 1.4270 - accuracy: 0.4754 - val_loss: 1.1578 - val_accuracy: 0.6285 - 2s/epoch - 14ms/step Epoch 13/30 121/121 - 2s - loss: 1.3754 - accuracy: 0.4926 - val_loss: 1.1651 - val_accuracy: 0.5771 - 2s/epoch - 19ms/step Epoch 14/30 121/121 - 2s - loss: 1.3504 - accuracy: 0.4978 - val_loss: 1.1966 - val_accuracy: 0.5771 - 2s/epoch - 19ms/step Epoch 15/30 121/121 - 1s - loss: 1.3491 - accuracy: 0.4994 - val_loss: 1.1654 - val_accuracy: 0.6121 - 1s/epoch - 12ms/step Epoch 16/30 121/121 - 1s - loss: 1.2910 - accuracy: 0.5152 - val_loss: 1.0915 - val_accuracy: 0.6051 - 1s/epoch - 9ms/step Epoch 17/30 121/121 - 1s - loss: 1.2437 - accuracy: 0.5344 - val_loss: 1.0110 - val_accuracy: 0.6308 - 1s/epoch - 9ms/step Epoch 18/30 121/121 - 1s - loss: 1.2255 - accuracy: 0.5407 - val_loss: 1.0835 - val_accuracy: 0.5935 - 1s/epoch - 10ms/step Epoch 19/30 121/121 - 1s - loss: 1.2104 - accuracy: 0.5412 - val_loss: 1.0117 - val_accuracy: 0.6589 - 1s/epoch - 10ms/step Epoch 20/30 121/121 - 1s - loss: 1.1667 - accuracy: 0.5571 - val_loss: 1.0476 - val_accuracy: 0.6425 - 1s/epoch - 9ms/step Epoch 21/30 121/121 - 1s - loss: 1.1573 - accuracy: 0.5617 - val_loss: 1.0146 - val_accuracy: 0.6402 - 1s/epoch - 10ms/step Epoch 22/30 121/121 - 1s - loss: 1.1191 - accuracy: 0.5633 - val_loss: 0.9795 - val_accuracy: 0.6495 - 1s/epoch - 9ms/step Epoch 23/30 121/121 - 1s - loss: 1.1530 - accuracy: 0.5615 - val_loss: 1.0085 - val_accuracy: 0.6472 - 1s/epoch - 11ms/step Epoch 24/30 121/121 - 1s - loss: 1.1082 - accuracy: 0.5711 - val_loss: 1.0331 - val_accuracy: 0.6402 - 1s/epoch - 11ms/step Epoch 25/30 121/121 - 1s - loss: 1.0824 - accuracy: 0.5799 - val_loss: 1.0147 - val_accuracy: 0.6285 - 1s/epoch - 11ms/step Epoch 26/30 121/121 - 1s - loss: 1.0367 - accuracy: 0.6007 - val_loss: 0.9732 - val_accuracy: 0.6799 - 1s/epoch - 10ms/step Epoch 27/30 121/121 - 1s - loss: 1.0592 - accuracy: 0.5883 - val_loss: 0.9948 - val_accuracy: 0.6542 - 1s/epoch - 10ms/step Epoch 28/30 121/121 - 1s - loss: 1.0205 - accuracy: 0.6075 - val_loss: 1.0231 - val_accuracy: 0.6519 - 1s/epoch - 10ms/step Epoch 29/30 121/121 - 1s - loss: 1.0346 - accuracy: 0.6059 - val_loss: 0.9646 - val_accuracy: 0.6706 - 1s/epoch - 10ms/step Epoch 30/30 121/121 - 1s - loss: 1.0156 - accuracy: 0.6158 - val_loss: 0.9575 - val_accuracy: 0.6776 - 1s/epoch - 10ms/step
# Evaluate the model on different performance metrics
# Accuracy
plt.plot(history_1.history['accuracy'])
plt.plot(history_1.history['val_accuracy'])
plt.title('Model Accuracy')
plt.ylabel('Accuracy')
plt.xlabel('Epoch')
plt.legend(['Train', 'Validation'], loc='upper left')
plt.show()
# Evaluate the Model1 on the test data
accuracy = model1.evaluate(X_test_normalized, y_test_encoded, verbose=2)
15/15 - 0s - loss: 0.9452 - accuracy: 0.6800 - 329ms/epoch - 22ms/step
from sklearn.metrics import classification_report
# Predict classes for the test set
y_pred = model1.predict(X_test_normalized)
y_pred_classes = np.argmax(y_pred, axis=1)
y_true = np.argmax(y_test_encoded, axis=1)
15/15 [==============================] - 0s 4ms/step
# Obtain the categorical values from y_test_encoded and y_pred
y_pred_arg=np.argmax(y_pred,axis=1)
y_test_arg=np.argmax(y_test_encoded,axis=1)
# Plot the Confusion Matrix using confusion matrix() function which is also predefined in tensorflow module
confusion_matrix = tf.math.confusion_matrix(y_test_arg, y_pred_arg)
f, ax = plt.subplots(figsize=(12, 12))
sns.heatmap(
confusion_matrix,
annot=True,
linewidths=.4,
fmt="d",
square=True,
ax=ax
)
# Set the labels to both the axes
ax.set_xlabel('Predicted labels');ax.set_ylabel('True labels');
ax.set_title('Confusion Matrix');
ax.xaxis.set_ticklabels(list(enc.classes_),rotation=40)
ax.yaxis.set_ticklabels(list(enc.classes_),rotation=20)
plt.show()
The confusion matrix shows varying levels of classification accuracy across different plant seedling species.
The model shows strong predictive power for some species but struggles with others, indicating a need for further refinement.
from sklearn import metrics
# Generate the classification report
cr=metrics.classification_report(y_test_arg, y_pred_arg)
print(cr)
precision recall f1-score support
0 0.00 0.00 0.00 26
1 0.75 0.77 0.76 39
2 0.54 0.48 0.51 29
3 0.85 0.82 0.83 61
4 0.50 0.05 0.08 22
5 0.64 0.67 0.65 48
6 0.55 0.97 0.70 65
7 0.80 0.73 0.76 22
8 0.68 0.75 0.72 52
9 0.62 0.43 0.51 23
10 0.75 0.82 0.78 50
11 0.77 0.71 0.74 38
accuracy 0.68 475
macro avg 0.62 0.60 0.59 475
weighted avg 0.65 0.68 0.65 475
Overall, the model seems useful but could benefit from targeted improvements in underperforming classes. There's a need for further tuning and possibly more balanced training data.
Reducing the Learning Rate:
Hint: Use ReduceLRonPlateau() function that will be used to decrease the learning rate by some factor, if the loss is not decreasing for some time. This may start decreasing the loss at a smaller learning rate. There is a possibility that the loss may still not decrease. This may lead to executing the learning rate reduction again in an attempt to achieve a lower loss.
# Reduce the learning rate and monitor val_accuracy
learning_rate_reduction = ReduceLROnPlateau(monitor='val_accuracy',
patience=3,
verbose=1,
factor=0.5,
min_lr=0.00001)
Remember, data augmentation should not be used in the validation/test data set.
# Clear backend
from tensorflow.keras import backend
backend.clear_session()
# Fix the seed for random number generators
import random
np.random.seed(42)
random.seed(42)
tf.random.set_seed(42)
# Set the rotation_range to 20 in the ImageDataGenerator
train_datagen = ImageDataGenerator(
rotation_range=20,
fill_mode='nearest'
)
# Build the improved model
# Initialize a sequential model
model2 = Sequential()
# Add the first conv layer
model2.add(Conv2D(64, (3, 3), activation='relu', padding="same", input_shape=(64, 64, 3)))
# Add max pooling to reduce the size of output of first conv layer
model2.add(MaxPooling2D((2, 2), padding = 'same'))
model2.add(Conv2D(32, (3, 3), activation='relu', padding="same"))
model2.add(MaxPooling2D((2, 2), padding = 'same'))
model2.add(BatchNormalization())
# Flatten the output of the conv layer after max pooling to make it ready for creating dense connections
model2.add(Flatten())
# Add a fully connected dense layer with 16 neurons
model2.add(Dense(16, activation='relu'))
# Add dropout with dropout_rate=0.3
model2.add(Dropout(0.3))
# Add the output layer with 12 neurons and activation function as softmax
model2.add(Dense(12, activation='softmax'))
# Initialize Adam Optimizer
opt = Adam()
# Compile the model
model2.compile(optimizer=opt, loss='categorical_crossentropy', metrics=['accuracy'])
# Generate the summary of the model
model2.summary()
Model: "sequential"
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
conv2d (Conv2D) (None, 64, 64, 64) 1792
max_pooling2d (MaxPooling2 (None, 32, 32, 64) 0
D)
conv2d_1 (Conv2D) (None, 32, 32, 32) 18464
max_pooling2d_1 (MaxPoolin (None, 16, 16, 32) 0
g2D)
batch_normalization (Batch (None, 16, 16, 32) 128
Normalization)
flatten (Flatten) (None, 8192) 0
dense (Dense) (None, 16) 131088
dropout (Dropout) (None, 16) 0
dense_1 (Dense) (None, 12) 204
=================================================================
Total params: 151676 (592.48 KB)
Trainable params: 151612 (592.23 KB)
Non-trainable params: 64 (256.00 Byte)
_________________________________________________________________
The summary of Model2 indicates a two-layer convolutional neural network with max pooling and batch normalization, designed for a 12-class classification problem. It has approximately 151.7K parameters, most of which are in the dense layer. The use of dropout suggests an attempt to mitigate overfitting. The model is relatively lightweight due to the small dense layer, which could impact the ability to learn complex features but will benefit training speed and memory usage.
# Fit the model on the train data
# Epochs
epochs = 30
# Batch size
batch_size = 64
# train_datagen = ImageDataGenerator(rescale=1. / 255)
history = model2.fit(train_datagen.flow(X_train_normalized, y_train_encoded,
batch_size=batch_size,
shuffle=False),
epochs=epochs,
steps_per_epoch=X_train_normalized.shape[0] // batch_size,
validation_data=(X_val_normalized, y_val_encoded),
verbose=1, callbacks=[learning_rate_reduction])
Epoch 1/30 60/60 [==============================] - 7s 77ms/step - loss: 2.1405 - accuracy: 0.2403 - val_loss: 2.3787 - val_accuracy: 0.2921 - lr: 0.0010 Epoch 2/30 60/60 [==============================] - 5s 85ms/step - loss: 1.6563 - accuracy: 0.4243 - val_loss: 2.2710 - val_accuracy: 0.2336 - lr: 0.0010 Epoch 3/30 60/60 [==============================] - 10s 156ms/step - loss: 1.4379 - accuracy: 0.4946 - val_loss: 2.2376 - val_accuracy: 0.2430 - lr: 0.0010 Epoch 4/30 60/60 [==============================] - 5s 85ms/step - loss: 1.4152 - accuracy: 0.5030 - val_loss: 2.0497 - val_accuracy: 0.3668 - lr: 0.0010 Epoch 5/30 60/60 [==============================] - 4s 71ms/step - loss: 1.2643 - accuracy: 0.5509 - val_loss: 2.0147 - val_accuracy: 0.3575 - lr: 0.0010 Epoch 6/30 60/60 [==============================] - 6s 104ms/step - loss: 1.1981 - accuracy: 0.5707 - val_loss: 1.7052 - val_accuracy: 0.4626 - lr: 0.0010 Epoch 7/30 60/60 [==============================] - 4s 71ms/step - loss: 1.1683 - accuracy: 0.5890 - val_loss: 1.4917 - val_accuracy: 0.6425 - lr: 0.0010 Epoch 8/30 60/60 [==============================] - 6s 98ms/step - loss: 1.0923 - accuracy: 0.6056 - val_loss: 1.3333 - val_accuracy: 0.5888 - lr: 0.0010 Epoch 9/30 60/60 [==============================] - 4s 70ms/step - loss: 1.0782 - accuracy: 0.6236 - val_loss: 1.2069 - val_accuracy: 0.6752 - lr: 0.0010 Epoch 10/30 60/60 [==============================] - 7s 109ms/step - loss: 1.0076 - accuracy: 0.6352 - val_loss: 1.2372 - val_accuracy: 0.5654 - lr: 0.0010 Epoch 11/30 60/60 [==============================] - 4s 72ms/step - loss: 1.0005 - accuracy: 0.6307 - val_loss: 1.0051 - val_accuracy: 0.6752 - lr: 0.0010 Epoch 12/30 60/60 [==============================] - ETA: 0s - loss: 0.9641 - accuracy: 0.6569 Epoch 12: ReduceLROnPlateau reducing learning rate to 0.0005000000237487257. 60/60 [==============================] - 4s 71ms/step - loss: 0.9641 - accuracy: 0.6569 - val_loss: 1.1051 - val_accuracy: 0.6636 - lr: 0.0010 Epoch 13/30 60/60 [==============================] - 6s 96ms/step - loss: 0.8946 - accuracy: 0.6749 - val_loss: 0.7785 - val_accuracy: 0.7547 - lr: 5.0000e-04 Epoch 14/30 60/60 [==============================] - 5s 77ms/step - loss: 0.8388 - accuracy: 0.6934 - val_loss: 0.7176 - val_accuracy: 0.7687 - lr: 5.0000e-04 Epoch 15/30 60/60 [==============================] - 6s 91ms/step - loss: 0.8403 - accuracy: 0.6965 - val_loss: 1.1571 - val_accuracy: 0.5981 - lr: 5.0000e-04 Epoch 16/30 60/60 [==============================] - 4s 69ms/step - loss: 0.8178 - accuracy: 0.6976 - val_loss: 0.7709 - val_accuracy: 0.7547 - lr: 5.0000e-04 Epoch 17/30 60/60 [==============================] - ETA: 0s - loss: 0.8099 - accuracy: 0.6994 Epoch 17: ReduceLROnPlateau reducing learning rate to 0.0002500000118743628. 60/60 [==============================] - 6s 94ms/step - loss: 0.8099 - accuracy: 0.6994 - val_loss: 0.7821 - val_accuracy: 0.7687 - lr: 5.0000e-04 Epoch 18/30 60/60 [==============================] - 4s 72ms/step - loss: 0.7780 - accuracy: 0.7156 - val_loss: 0.7268 - val_accuracy: 0.7710 - lr: 2.5000e-04 Epoch 19/30 60/60 [==============================] - 7s 119ms/step - loss: 0.7722 - accuracy: 0.7095 - val_loss: 0.7398 - val_accuracy: 0.7757 - lr: 2.5000e-04 Epoch 20/30 60/60 [==============================] - 4s 72ms/step - loss: 0.7541 - accuracy: 0.7174 - val_loss: 0.7417 - val_accuracy: 0.7850 - lr: 2.5000e-04 Epoch 21/30 60/60 [==============================] - 6s 100ms/step - loss: 0.7483 - accuracy: 0.7211 - val_loss: 0.7051 - val_accuracy: 0.7921 - lr: 2.5000e-04 Epoch 22/30 60/60 [==============================] - 4s 70ms/step - loss: 0.7342 - accuracy: 0.7349 - val_loss: 0.7199 - val_accuracy: 0.7967 - lr: 2.5000e-04 Epoch 23/30 60/60 [==============================] - 6s 100ms/step - loss: 0.7401 - accuracy: 0.7238 - val_loss: 0.9379 - val_accuracy: 0.7173 - lr: 2.5000e-04 Epoch 24/30 60/60 [==============================] - 5s 78ms/step - loss: 0.7252 - accuracy: 0.7285 - val_loss: 0.7016 - val_accuracy: 0.8131 - lr: 2.5000e-04 Epoch 25/30 60/60 [==============================] - 4s 72ms/step - loss: 0.7367 - accuracy: 0.7269 - val_loss: 0.7203 - val_accuracy: 0.7850 - lr: 2.5000e-04 Epoch 26/30 60/60 [==============================] - 6s 100ms/step - loss: 0.6985 - accuracy: 0.7367 - val_loss: 0.6804 - val_accuracy: 0.8037 - lr: 2.5000e-04 Epoch 27/30 60/60 [==============================] - ETA: 0s - loss: 0.7331 - accuracy: 0.7359 Epoch 27: ReduceLROnPlateau reducing learning rate to 0.0001250000059371814. 60/60 [==============================] - 5s 79ms/step - loss: 0.7331 - accuracy: 0.7359 - val_loss: 0.7410 - val_accuracy: 0.7710 - lr: 2.5000e-04 Epoch 28/30 60/60 [==============================] - 4s 72ms/step - loss: 0.6831 - accuracy: 0.7497 - val_loss: 0.6741 - val_accuracy: 0.8107 - lr: 1.2500e-04 Epoch 29/30 60/60 [==============================] - 6s 108ms/step - loss: 0.6948 - accuracy: 0.7409 - val_loss: 0.7446 - val_accuracy: 0.7664 - lr: 1.2500e-04 Epoch 30/30 60/60 [==============================] - ETA: 0s - loss: 0.6889 - accuracy: 0.7470 Epoch 30: ReduceLROnPlateau reducing learning rate to 6.25000029685907e-05. 60/60 [==============================] - 4s 71ms/step - loss: 0.6889 - accuracy: 0.7470 - val_loss: 0.7791 - val_accuracy: 0.7687 - lr: 1.2500e-04
# Evaluate the model on different performance metrics
# Accuracy
plt.plot(history.history['accuracy'])
plt.plot(history.history['val_accuracy'])
plt.title('Model Accuracy')
plt.ylabel('Accuracy')
plt.xlabel('Epoch')
plt.legend(['Train', 'Validation'], loc='upper left')
plt.show()
# Evaluate the Model2 on the test data
accuracy = model2.evaluate(X_test_normalized, y_test_encoded, verbose=2)
15/15 - 0s - loss: 0.8922 - accuracy: 0.7389 - 254ms/epoch - 17ms/step
# Predict the output probabilities for each category
y_pred = model2.predict(X_test_normalized)
15/15 [==============================] - 0s 3ms/step
# Obtain the categorical values from y_test_encoded and y_pred
y_pred_arg=np.argmax(y_pred,axis=1)
y_test_arg=np.argmax(y_test_encoded,axis=1)
# Plot the Confusion Matrix using confusion matrix() function which is also predefined in tensorflow module
confusion_matrix = tf.math.confusion_matrix(y_test_arg,y_pred_arg)
f, ax = plt.subplots(figsize=(12, 12))
sns.heatmap(
confusion_matrix,
annot=True,
linewidths=.4,
fmt="d",
square=True,
ax=ax
)
# Set the labels to both the axes
ax.set_xlabel('Predicted labels');ax.set_ylabel('True labels');
ax.set_title('Confusion Matrix');
ax.xaxis.set_ticklabels(list(enc.classes_),rotation=40)
ax.yaxis.set_ticklabels(list(enc.classes_),rotation=20)
plt.show()
# Generate the classification report
cr = metrics.classification_report(y_test_arg, y_pred_arg)
print(cr)
precision recall f1-score support
0 0.41 0.27 0.33 26
1 0.77 0.85 0.80 39
2 0.79 0.66 0.72 29
3 0.98 0.87 0.92 61
4 0.72 0.59 0.65 22
5 0.85 0.69 0.76 48
6 0.68 0.74 0.71 65
7 0.46 1.00 0.63 22
8 0.68 0.77 0.72 52
9 0.70 0.61 0.65 23
10 0.91 0.78 0.84 50
11 0.77 0.79 0.78 38
accuracy 0.74 475
macro avg 0.73 0.72 0.71 475
weighted avg 0.76 0.74 0.74 475
Comment on the final model you have selected and use the same in the below code to visualize the image.
# Assuming X_test_normalized is your test data already normalized and Model2 is your trained model
y_pred_model2 = model2.predict(X_test_normalized)
# Convert the probabilities to class labels
y_pred_classes_model2 = np.argmax(y_pred_model2, axis=1)
# Convert the class labels to one-hot encoded form
y_pred_one_hot = enc.transform(y_pred_classes_model2.reshape(-1, 1))
# Show the predicted labels in their original string format
predicted_labels = enc.inverse_transform(y_pred_one_hot)
15/15 [==============================] - 0s 3ms/step
# Visualize the predicted and correct label of images from test data
plt.figure(figsize=(2,2))
plt.imshow(X_test[2])
plt.show()
# Predict the test data using the final model selected
print('Predicted Label', enc.inverse_transform(model2.predict(X_test_normalized[2].reshape(1,64,64,3))))
print('True Label', enc.inverse_transform(y_test_encoded)[2])
plt.figure(figsize=(2,2))
plt.imshow(X_test[33])
plt.show()
# Predict the test data using the final model selected
print('Predicted Label', enc.inverse_transform(model2.predict(X_test_normalized[33].reshape(1,64,64,3))))
print('True Label', enc.inverse_transform(y_test_encoded)[33])
plt.figure(figsize=(2,2))
plt.imshow(X_test[59])
plt.show()
# Predict the test data using the final model selected
print('Predicted Label', enc.inverse_transform(model2.predict(X_test_normalized[59].reshape(1,64,64,3))))
print('True Label', enc.inverse_transform(y_test_encoded)[59])
plt.figure(figsize=(2,2))
plt.imshow(X_test[36])
plt.show()
# Predict the test data using the final model selected
print('Predicted Label', enc.inverse_transform(model2.predict(X_test_normalized[36].reshape(1,64,64,3))))
print('True Label', enc.inverse_transform(y_test_encoded)[36])
1/1 [==============================] - 0s 174ms/step Predicted Label ['Small-flowered Cranesbill'] True Label Small-flowered Cranesbill
1/1 [==============================] - 0s 18ms/step Predicted Label ['Cleavers'] True Label Cleavers
1/1 [==============================] - 0s 17ms/step Predicted Label ['Common Chickweed'] True Label Common Chickweed
1/1 [==============================] - 0s 22ms/step Predicted Label ['Shepherds Purse'] True Label Shepherds Purse